package lmscript.postscript.io;

import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;

public class ASCII85InputStream extends InputStream implements ASCII85 {

	private PushbackInputStream in;
	private int line;
	private int[] c5 = new int[5];
	private int[] b4 = new int[4];
	private int bIndex = 0;
	private int cIndex = 0;
	private boolean eod = false;

	public ASCII85InputStream(InputStream in) {
		super();
		if (in instanceof PushbackInputStream) {
			this.in = (PushbackInputStream) in;
		} else {
			this.in = new PushbackInputStream(in);
		}
	}

	@Override
	public int read() throws IOException {
		if (bIndex >= 4) {
			if (eod) {
				return -1;
			}
			this.readTuple();
			bIndex = 0;
		}

		int ch = b4[bIndex];
		bIndex++;
		return ch;
	}

	public int lineCount() {
		return this.line;
	}
	
	private int readTuple() throws IOException {
		while (!eod && cIndex < 5) {
			int c = this.in.read();
			switch (c) {
			case -1:
				throw new IOException("unexcepted EOF!");
			case '~': {
				int next = this.in.read();
				if (next != '>') {
					throw new IOException("unexcepted ASCII85 EOD: " + c + next);
				} else {
					eod = true;
					break;
				}
			}
			case '\r': {
				this.line++;
				c = this.in.read();
				if (c != '\n') {
					this.in.unread(c);
				}
				break;
			}
			case ' ':
			case '\t':
			case '\f':
			case 0x00:
				break;
			case 'z': {
				b4[0] = b4[1] = b4[2] = b4[3] = '!';
				return 4;
			}
			default: {
				c5[cIndex] = c;
				cIndex++;
				break;
			}
			}
		}
		if (cIndex > 0) {
			for (int i = 0; i < 5; i++) {
				if (i > cIndex) {
					c5[i] = '!';
				} else {
					c5[i] -= '!';
				}
			}
			long d = (c5[0] * a85p4 + c5[1] * a85p3 + c5[2] * a85p2 + c5[3] * a85p1 + c5[4]) & 0x00000000FFFFFFFFL;
			b4[0] = (int) ((d >> 24) & 0x00FF);
			b4[1] = (int) ((d >> 16) & 0x00FF);
			b4[2] = (int) ((d >> 8) & 0x00FF);
			b4[3] = (int) (d & 0x00FF);
		}
		return 4;
	}
}
